package com.mdd.front.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mdd.common.core.AjaxResult;
import com.mdd.common.core.PageResult;
import com.mdd.common.core.WxResult;
import com.mdd.common.core.pay.WxPrePayForm;
import com.mdd.common.entity.UserInvitation;
import com.mdd.common.entity.coupon.Coupon;
import com.mdd.common.entity.order.*;
import com.mdd.common.entity.user.UserAuth;
import com.mdd.common.enums.CodeEnum;
import com.mdd.common.enums.CouponEnum;
import com.mdd.common.enums.HttpEnum;
import com.mdd.common.enums.OrderStatusEnum;
import com.mdd.common.mapper.order.*;
import com.mdd.common.mapper.user.UserAuthMapper;
import com.mdd.common.mapper.user.UserInvitationMapper;
import com.mdd.common.util.*;
import com.mdd.front.LikeFrontThreadLocal;
import com.mdd.front.service.ICouponService;
import com.mdd.front.service.IOrderService;
import com.mdd.common.util.WxPayUtils;
import com.mdd.front.validate.commons.PageValidate;
import com.mdd.front.validate.order.OrderCreateValidate;
import com.mdd.front.validate.order.OrderDetailValidate;
import com.mdd.front.vo.order.AddressVo;
import com.mdd.front.vo.order.MenuMealVo;
import com.mdd.front.vo.order.OrderVo;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.*;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 订单服务
 *
 * @author shenjian
 * @createTime 2023年02月20日 23:05:00
 */
@Service
@Log4j2
public class OrderServiceImpl implements IOrderService {

    private static final String REDIS_KEY_ORDER_PREFIX = "ORDER_";
    private static final String ORDER_RESULT_CODE_STR = "code";
    private static final String ORDER_RESULT_MSG_STR = "msg";
    private static final String ORDER_PRE_PAY = "0";
    private static final String ORDER_PAY_COMPLETE = "1";
    private static final String ORDER_PAY_FAIL = "-1";
    private static final String ORDER_PAYING = "-999";

    /** 退款分布式锁前缀  */
    private static final String ORDER_REFUND_LOCK_KEY_PREFIX = "ORDER:REFUND:LOCK";

    /** 领取优惠券分布式锁Key */
    private static final String ORDER_CREATE_LOCK_KEY_PREFIX = "ORDER:CREATE:LOCK";

    /** 领取优惠券分布式锁有效时间 */
    private static final Long ORDER_CREATE_LOCK_REDIS_WAIT_TIME = 5L;

    /** 退款分布式锁等待时间，单位秒 */
    private static final Long ORDER_REFUND_LOCK_REDIS_WAIT_TIME = 5L;

    @Resource
    OrderMapper orderMapper;
    @Resource
    MenuMealMapper menuMealMapper;
    @Resource
    CouponMapper couponMapper;
    @Resource
    UserAuthMapper userAuthMapper;
    @Resource
    OrderMealMapper orderMealMapper;
    @Resource
    OrderDetailMapper orderDetailMapper;
    @Resource
    AddressMapper addressMapper;
    @Resource
    MansionMapper manaMapper;
    @Resource
    RefundMapper refundMapper;
    @Resource
    UserInvitationMapper userInvitationMapper;
    @Resource
    ICouponService iCouponService;

    @Override
    //@Async("createOrderExecutor")
    @Transactional(rollbackFor = Exception.class)
    public void createOrder(OrderCreateValidate createValidate, Integer userId) {
        // 缓存key
        String redidKey = REDIS_KEY_ORDER_PREFIX + createValidate.getOrderNo();
        JSONObject json = new JSONObject();
        json.put(ORDER_RESULT_CODE_STR, ORDER_PAYING);
        RedisUtils.set(redidKey, json, 900);
        //尝试加锁
        String lockKey = ORDER_CREATE_LOCK_KEY_PREFIX + userId + Math.random();
        boolean lock = RedisLockUtils.tryLock(lockKey, createValidate.getOrderNo(), Duration.ofSeconds(ORDER_CREATE_LOCK_REDIS_WAIT_TIME));
        Assert.isTrue(lock, "请稍后再试");
        try {
            // 查询人员的openId
            UserAuth userAuth = userAuthMapper.selectOne(new QueryWrapper<UserAuth>()
                    .eq("user_id", userId)
                    .eq("client", createValidate.getClient())
                    .last("limit 1"));
            Assert.notNull(userAuth, "用户未绑定");
            // 查询套餐数据
            List<OrderDetailValidate> menuMealList = createValidate.getMenuMealList();
            Map<Integer, List<OrderDetailValidate>> menuMealMap = menuMealList
                    .stream()
                    .collect(Collectors.groupingBy(OrderDetailValidate::getMenuMealId));
            List<MenuMeal> menuMeals = menuMealMapper.selectList(new QueryWrapper<MenuMeal>()
                    .in("id", menuMealMap.keySet()));
            Map<Integer, List<MenuMeal>> menuMap = menuMeals
                    .stream()
                    .collect(Collectors.groupingBy(MenuMeal::getMenuId));
            Assert.isTrue(menuMap.size() == 1, "选取套餐数据存在问题");
            // 计算总金额
            AtomicInteger totalAmount = new AtomicInteger();
            menuMeals.forEach(menuMeal -> {
                // 判断上下架状态
                Assert.isTrue(menuMeal.getStatus().equals(1), "订单里包含了已下架的套餐，请重新下单。");
                // 查询数量
                Integer quantity =
                        menuMealMap.get(menuMeal.getId()).get(0).getQuantity();
                // 金额
                double sub = ArithUtils.sub(menuMeal.getPrice(), menuMeal.getPreferentialAmount());
                totalAmount.addAndGet(Math.toIntExact(Math.round(ArithUtils.mul(sub, quantity))));
            });
            // 查询优惠券
            int couponAmount = 0;
            if (createValidate.getCouponId() != null) {
                Coupon coupon = couponMapper.selectById(createValidate.getCouponId());
                Assert.notNull(coupon, "优惠券不存在");
                Assert.isTrue(coupon.getUserId().equals(userId), "下单数据异常");
                Assert.isFalse(coupon.getStatus() == 1, "优惠券已使用");
                Assert.isFalse(coupon.getStatus() == 2, "优惠券已失效");
                Assert.isTrue(coupon.getStartTime() <= TimeUtils.timestamp(), "优惠券暂不能使用");
                Assert.isTrue(coupon.getEndTime() > TimeUtils.timestamp(), "优惠券暂已过期");
                Assert.isTrue(totalAmount.get() >= coupon.getThreshold(), "未达到优惠券使用门槛");
                couponAmount = coupon.getCouponAmount();
            }

            // 总金额
            int actualAmount = Math.max((totalAmount.get() - couponAmount), 0);
            // 前端计算金额
            Assert.isTrue(actualAmount == AmountUtils.changeY2F(createValidate.getPrice()), "疑似攻击行为, 正在报警...");
            // 保存订单
            saveOrder(createValidate, menuMeals, userId, totalAmount.get(), couponAmount, actualAmount);
            if (actualAmount > 0) {
                // 发起预支付
                PrepayWithRequestPaymentResponse response = wxPay(actualAmount, createValidate.getOrderNo(), userAuth.getOpenid());
                log.debug("订单号 -> [{}], 调用微信发起支付信息 -> [{}]", createValidate.getOrderNo(), response);
                json.put(ORDER_RESULT_CODE_STR, ORDER_PRE_PAY);
                json.put(ORDER_RESULT_MSG_STR, response);
            } else {
                Transaction transaction = new Transaction();
                transaction.setOutTradeNo(createValidate.getOrderNo());
                // 金额为0时，无需调用支付
                paySuccess(transaction);
                json.put(ORDER_RESULT_CODE_STR, ORDER_PAY_COMPLETE);
                json.put(ORDER_RESULT_MSG_STR, "支付成功");
            }
        } catch (MybatisPlusException mpException) {
            log.error("【微信预支付报错 - [{}]】, errorMag - [{}]", createValidate.getOrderNo(), mpException);
            json.put(ORDER_RESULT_CODE_STR, ORDER_PAY_FAIL);
            json.put(ORDER_RESULT_MSG_STR, mpException.getMessage());
            //手动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        } catch (Exception e) {
            log.error("业务处理发生异常，错误信息：", e);
            log.error("【微信预支付报错 - [{}]】, errorMag - [{}]", createValidate.getOrderNo(), e);
            json.put(ORDER_RESULT_CODE_STR, ORDER_PAY_FAIL);
            json.put(ORDER_RESULT_MSG_STR, "支付发生错误，请联系管理员");
            //手动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        } finally {
            RedisUtils.set(redidKey, json, 900);
            //执行完毕之后，手动将锁释放
            RedisLockUtils.releaseLock(lockKey, createValidate.getOrderNo());
        }
        //任务执行成功，清除锁
        RedisUtils.del(lockKey);
    }

    /**
     * 查询缓存中的微信预支付单号
     *
     * @param orderNo 订单号
     * @return AjaxResult<Object>
     */
    @Override
    public AjaxResult<Object> queryWxPrePayId(String orderNo) {
        String redidKey = REDIS_KEY_ORDER_PREFIX + orderNo;
        Object object = RedisUtils.get(redidKey);
        if (null == object) {
            Order order = orderMapper.selectOne(new QueryWrapper<Order>()
                    .eq("order_no", orderNo)
                    .last("limit 1"));
            if (order.getStatus().equals(OrderStatusEnum.ORDERED.getCode())) {
                return AjaxResult.failed(3001, "订单支付过期，请重新下单");
            } else {
                return AjaxResult.success(HttpEnum.SUCCESS.getMsg(), "支付成功");
            }
        }
        JSONObject jsonObject = (JSONObject) object;
        if (jsonObject.get(ORDER_RESULT_CODE_STR).equals(ORDER_PRE_PAY)) {
            return AjaxResult.success(HttpEnum.SUCCESS.getMsg(), jsonObject.get(ORDER_RESULT_MSG_STR));
        } else if (jsonObject.get(ORDER_RESULT_CODE_STR).equals(ORDER_PAYING)) {
            return AjaxResult.failed(3000, "生成预订单中....");
        } else if (jsonObject.get(ORDER_RESULT_CODE_STR).equals(ORDER_PAY_COMPLETE)) {
            return AjaxResult.success(3005, HttpEnum.SUCCESS.getMsg(), "支付成功");
        } else {
            return AjaxResult.failed(3002, jsonObject.get(ORDER_RESULT_MSG_STR).toString());
        }
    }

    @Override
    public WxResult payCallback(JSONObject jsonObject, HttpHeaders headers) {
        log.debug("微信支付返回结果 -> [{}], 请求头 -> [{}]", jsonObject, headers);
        try {
            Transaction transaction = WxPayUtils.wxCallback(
                    headers,
                    jsonObject.toJSONString(),
                    Transaction.class);
            log.debug("微信支付返回结果 -> [{}]", transaction);
            // 修改数据库状态
            paySuccess(transaction);
        } catch (Exception e) {
            log.error("微信支付回调报错", e);
        }
        return WxResult.builder().code("SUCCESS").build();
    }

    public void pullWxPay(String orderNo) {
        Transaction transaction = WxPayUtils.queryOrderByNo(orderNo);
        log.debug("微信支付返回结果 -> [{}]", transaction);
        // 修改数据库状态
        paySuccess(transaction);
    }

    @Override
    public PageResult<OrderVo> searchOrderList(PageValidate pageValidate, Integer status) {
        Integer userId = LikeFrontThreadLocal.getUserId();
        Integer pageNo = pageValidate.getPageNo();
        Integer pageSize = pageValidate.getPageSize();
        List<OrderStatusEnum> statusEnums = OrderStatusEnum.getStatusByQuery(status);

        Page<Order> orderPage = orderMapper.selectPage(new Page<>(pageNo, pageSize),
                new QueryWrapper<Order>()
                        .eq("user_id", userId)
                        .in("status", statusEnums.stream().map(OrderStatusEnum::getCode).collect(Collectors.toList()))
                        .orderByDesc("create_time"));
        List<OrderVo> orderList = new ArrayList<>();
        if (!orderPage.getRecords().isEmpty()) {
            List<Integer> orderIds = orderPage.getRecords().stream()
                    .map(Order::getId)
                    .collect(Collectors.toList());
            List<OrderDetail> orderDetails = orderDetailMapper.selectList(
                    new QueryWrapper<OrderDetail>()
                            .in("order_id", orderIds)
            );
            Map<Integer, List<OrderDetail>> orderDetailMap = orderDetails.stream()
                    .collect(Collectors.groupingBy(OrderDetail::getOrderId));

            for (Order order : orderPage.getRecords()) {
                List<OrderDetail> details = orderDetailMap.get(order.getId());
                OrderVo orderVo = new OrderVo();
                BeanUtils.copyProperties(order, orderVo);
                orderVo.setAmount(AmountUtils.changeF2Y(order.getActualAmount()));
                // 查询地址
                if (order.getAddressId() != null) {
                    Address address = addressMapper.selectById(order.getAddressId());
                    if (address != null) {
                        Mansion mansion = manaMapper.selectById(address.getMansionId());
                        AddressVo addressVo = new AddressVo();
                        BeanUtils.copyProperties(address, addressVo);
                        addressVo.setMansionName(mansion.getName());
                        orderVo.setAddress(addressVo);
                    }
                }
                orderVo.setMealList(details.stream()
                        .map(detail -> MenuMealVo.builder()
                                .menuId(detail.getMenuId())
                                .mealId(detail.getMealId())
                                .menuMealId(detail.getMenuMealId())
                                .mealName(detail.getMealName())
                                .mealPic(detail.getMealPic())
                                .mealDescribe(detail.getMealDescribe())
                                .quantity(detail.getMealNumber())
                                .actualAmount(AmountUtils.changeF2Y(detail.getUnitPrice()))
                                .build()).collect(Collectors.toList()));
                orderList.add(orderVo);
            }
        }
        return PageResult.iPageHandle(orderPage.getTotal(), orderPage.getCurrent(), orderPage.getSize(), orderList);
    }

    @Override
    public OrderVo findOrderById(Integer orderId) {
        OrderVo orderVo = new OrderVo();
        Order order = orderMapper.selectById(orderId);
        BeanUtils.copyProperties(order, orderVo);
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(
                new QueryWrapper<OrderDetail>()
                        .eq("order_id", orderId)
        );
        // 查询地址
        Address address = addressMapper.selectById(order.getAddressId());
        if (address != null) {
            Mansion mansion = manaMapper.selectById(address.getMansionId());
            AddressVo addressVo = new AddressVo();
            BeanUtils.copyProperties(address, addressVo);
            addressVo.setMansionName(mansion.getName());
            orderVo.setAddress(addressVo);
        }
        orderVo.setCreateTimeStr(TimeUtils.timestampToDate(order.getCreateTime()));
        orderVo.setApplyRefundTimeStr(TimeUtils.timestampToDate(order.getApplyRefundTime()));
        orderVo.setRefundTimeStr(TimeUtils.timestampToDate(order.getRefundTime()));
        orderVo.setMealList(orderDetails.stream()
                .map(detail -> MenuMealVo.builder()
                        .menuId(detail.getMenuId())
                        .mealId(detail.getMealId())
                        .menuMealId(detail.getMenuMealId())
                        .mealName(detail.getMealName())
                        .mealPic(detail.getMealPic())
                        .mealDescribe(detail.getMealDescribe())
                        .quantity(detail.getMealNumber())
                        .actualAmount(AmountUtils.changeF2Y(detail.getUnitPrice()))
                        .build()).collect(Collectors.toList()));
        return orderVo;
    }

    @Override
    public void applyRefund(Integer orderId) {
        Integer userId = LikeFrontThreadLocal.getUserId();
        Order order = orderMapper.selectById(orderId);
        Assert.notNull(order, "订单数据不正确");
        Assert.isTrue(order.getUserId().equals(userId), "订单数据不正确");
        Assert.isFalse(order.getStatus().equals(OrderStatusEnum.ORDERED.getCode()), ":( 没有支付，你退什么款。");
        Assert.isFalse(order.getStatus().equals(OrderStatusEnum.DURING_MEAL.getCode()), "正在出餐中，暂不支持退款。请联系客服。");
        Assert.isFalse(order.getStatus().equals(OrderStatusEnum.SHIPPING.getCode()), "订单已配送，暂不支持退款。请联系客服。");
        Assert.isFalse(order.getStatus().equals(OrderStatusEnum.DELIVERED.getCode()), "订单已送达，暂不支持退款。请联系客服。");
        Assert.isFalse(order.getStatus().equals(OrderStatusEnum.COMPLETED.getCode()), "订单已完成，暂不支持退款。请联系客服。");
        Assert.isFalse(order.getStatus().equals(OrderStatusEnum.APPLY_REFUNDED.getCode()), ":( 做点生意不容易，你都申请退款了。还要来。");
        Assert.isFalse(order.getStatus().equals(OrderStatusEnum.REFUNDED.getCode()), ":( 行行好吧，你都退款了。还要继续。");
        //尝试加锁
        String lockKey = ORDER_REFUND_LOCK_KEY_PREFIX + order.getOrderNo();
        boolean lock = RedisLockUtils.tryLock(lockKey, order.getOrderNo(), Duration.ofSeconds(ORDER_REFUND_LOCK_REDIS_WAIT_TIME));
        Assert.isTrue(lock, "正在处理中，请勿重复提交！");

        try {
            //继续执行后续流程
            if (order.getStatus().equals(OrderStatusEnum.PAID.getCode())) {
                Long timestamp = TimeUtils.timestamp();
                // 判断订单金额
                if (order.getActualAmount() > 0) {
                    Refund refund = this.wxApplyRefund(order);
                    log.debug("用户[{}]申请微信退款返回:[{}]", userId, refund);
                    Assert.notNull(refund, "调用微信退款失败");
                    order.setStatus(OrderStatusEnum.APPLY_REFUNDED.getCode());
                    order.setApplyRefundTime(timestamp);
                    order.setUpdateTime(timestamp);
                    this.orderMapper.updateById(order);
                    if (refund.getStatus().equals(Status.SUCCESS)) {
                        Date createDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(refund.getCreateTime());
                        Date successDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(refund.getSuccessTime());
                        refundSuccess(refund.getAmount().getRefund().intValue(), createDate, successDate,
                                refund.getOutTradeNo(), refund.getTransactionId(), refund.getOutRefundNo(),
                                refund.getRefundId(), refund.getUserReceivedAccount(), order);
                    }
                } else {
                    order.setStatus(OrderStatusEnum.REFUNDED.getCode());
                    order.setApplyRefundTime(timestamp);
                    order.setRefundTime(timestamp);
                    order.setUpdateTime(timestamp);
                    this.orderMapper.updateById(order);
                    Date date = new Date();
                    refundSuccess(order.getActualAmount(), date, date,
                            order.getOrderNo(), null, null,
                            null, null, order);
                }
            }
            //任务执行成功，清除submitToken缓存
            RedisUtils.del(lockKey);
        } catch (Throwable e) {
            log.error("业务处理发生异常，错误信息：", e);
        } finally {
            //执行完毕之后，手动将锁释放
            RedisLockUtils.releaseLock(lockKey, order.getOrderNo());
        }
    }

    @Override
    public WxResult refundCallback(JSONObject jsonObject, HttpHeaders headers) {
        log.debug("微信退款返回结果 -> [{}], 请求头 -> [{}]", jsonObject, JSONObject.toJSONString(headers));
        RefundNotification refund = WxPayUtils.wxCallback(
                headers,
                jsonObject.toString(),
                RefundNotification.class);
        log.debug("微信退款返回结果 -> [{}]", refund);
        Order order = orderMapper.selectOne(new QueryWrapper<Order>()
                .eq("order_no", refund.getOutTradeNo())
                .last("limit 1"));
        //尝试加锁
        String lockKey = ORDER_REFUND_LOCK_KEY_PREFIX + refund.getOutTradeNo();
        boolean lock = RedisLockUtils.tryLock(lockKey, refund.getOutTradeNo(), Duration.ofSeconds(ORDER_REFUND_LOCK_REDIS_WAIT_TIME));
        Assert.isTrue(lock, "正在处理中，请勿重复提交！");
        try {
            //继续执行后续流程
            if (order.getStatus().equals(OrderStatusEnum.APPLY_REFUNDED.getCode())) {
                Long timestamp = TimeUtils.timestamp();
                log.debug("用户[{}]微信退款返回:[{}]", order.getUserId(), refund);
                Assert.notNull(refund, "调用微信退款失败");
                order.setStatus(OrderStatusEnum.REFUNDED.getCode());
                order.setRefundTime(timestamp);
                order.setUpdateTime(timestamp);
                this.orderMapper.updateById(order);
                final DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
                Date createDate = null;
                if (refund.getCreateTime() != null) {
                    ZonedDateTime zonedDateTime = ZonedDateTime.parse(refund.getCreateTime(), formatter);
                    createDate = Date.from(zonedDateTime.toInstant());
                }
                Date successDate = null;
                if (refund.getSuccessTime() != null) {
                    ZonedDateTime zonedDateTime = ZonedDateTime.parse(refund.getSuccessTime(), formatter);
                    successDate = Date.from(zonedDateTime.toInstant());
                }
                refundSuccess(refund.getAmount().getRefund().intValue(), createDate, successDate,
                        refund.getOutTradeNo(), refund.getTransactionId(), refund.getOutRefundNo(),
                        refund.getRefundId(), refund.getUserReceivedAccount(), order);
            }
            //任务执行成功，清除加锁缓存
            RedisUtils.del(lockKey);
        } catch (Throwable e) {
            log.error("业务处理发生异常，错误信息：", e);
        } finally {
            //执行完毕之后，手动将锁释放
            RedisLockUtils.releaseLock(lockKey, refund.getOutTradeNo());
        }
        return WxResult.builder().code("SUCCESS").build();
    }

    private void paySuccess(Transaction transaction) {
        Long timestamp = TimeUtils.timestamp();
        Order order = orderMapper.selectOne(new QueryWrapper<Order>()
                .eq("order_no", transaction.getOutTradeNo())
                .le("status", OrderStatusEnum.ORDERED.getCode())
                .last("limit 1"));
        // 判断如果状态变更为已失效 需要变更库存 是否使用优惠券
        if(order.getStatus().equals(OrderStatusEnum.EXPIRED.getCode())) {
            if (order.getCouponId() != null && order.getCouponId() != 0) {
                Coupon coupon = couponMapper.selectById(order.getCouponId());
                coupon.setStatus(1);
                coupon.setUseTime(order.getCreateTime());
                couponMapper.updateById(coupon);
            }
            // 消耗库存
            List<OrderDetail> detailList = orderDetailMapper.selectList(
                    new QueryWrapper<OrderDetail>()
                            .eq("order_id", order.getId()));
            detailList.forEach(detail -> {
                MenuMeal menuMeal = this.menuMealMapper.selectById(detail.getMenuMealId());
                menuMeal.setStock(menuMeal.getStock() - detail.getMealNumber());
                menuMeal.setSalesNum(menuMeal.getSalesNum() + detail.getMealNumber());
                menuMeal.setUpdateTime(timestamp);
                this.menuMealMapper.updateById(menuMeal);
            });
        }
        Assert.isTrue((transaction.getTransactionId() != null || order.getActualAmount() == 0), "微信未收到款项");
        order.setStatus(OrderStatusEnum.PAID.getCode());
        order.setTransactionId(transaction.getTransactionId());
        order.setUpdateTime(timestamp);
        order.setPayTime(timestamp);
        orderMapper.updateById(order);
        // 判断是否是第一单
        Long count = orderMapper.selectCount(new QueryWrapper<Order>()
                .ne("order_no", transaction.getOutTradeNo())
                .eq("user_id", order.getUserId()));
        if (count == 0) {
            // 邀请人
            UserInvitation invitation = userInvitationMapper.selectOne(new QueryWrapper<UserInvitation>()
                    .eq("invitee_id", order.getUserId())
                    .last("limit 1"));
            if (invitation != null) {
                // 发放优惠券
                iCouponService.issueSystemCoupon(order.getUserId(), CouponEnum.INVITEE_ORDER.getKey());
                iCouponService.issueSystemCoupon(invitation.getInviterId(), CouponEnum.INVITER_ORDER.getKey());
                invitation.setHasOrder(1);
                invitation.setUpdateTime(timestamp);
                userInvitationMapper.updateById(invitation);
            }
        }
    }

    private void refundSuccess(Integer amount, Date createTime, Date successTime,
                               String orderNo, String transactionId, String refundNo,
                               String refundId, String userReceivedAccount, Order order) {
        // 退款表
        com.mdd.common.entity.order.Refund refund = com.mdd.common.entity.order.Refund
                .builder()
                .orderId(order.getId())
                .orderNo(orderNo)
                .transactionId(transactionId)
                .refundNo(refundNo)
                .refundAmount(order.getActualAmount())
                .refundActualAmount(amount)
                .wxRefundId(refundId)
                .status(1)
                .userReceivedAccount(userReceivedAccount)
                .build();
        if (createTime != null) {
            refund.setRefundTime(createTime.getTime());
        }
        if (successTime != null) {
            refund.setRefundTime(successTime.getTime());
        }
        this.refundMapper.insert(refund);
        // 如果使用优惠券回退
        if (order.getCouponId() != null && order.getCouponId() != 0) {
            Coupon coupon = couponMapper.selectById(order.getCouponId());
            coupon.setStatus(0);
            coupon.setUseTime(null);
            couponMapper.updateById(coupon);
        }
        // 恢复库存
        List<OrderDetail> detailList = orderDetailMapper.selectList(
                new QueryWrapper<OrderDetail>()
                        .eq("order_id", order.getId()));
        detailList.forEach(detail -> {
            MenuMeal menuMeal = this.menuMealMapper.selectById(detail.getMenuMealId());
            menuMeal.setStock(menuMeal.getStock() + detail.getMealNumber());
            menuMeal.setSalesNum(menuMeal.getSalesNum() - detail.getMealNumber());
            menuMeal.setUpdateTime(TimeUtils.timestamp());
            this.menuMealMapper.updateById(menuMeal);
        });
    }

    /**
     * 发起微信预支付
     *
     * @param actualAmount 实际支付金额
     * @param orderNo      订单号
     * @param openId       付款人openId
     * @return prepayId 微信预支付ID
     */
    private PrepayWithRequestPaymentResponse wxPay(Integer actualAmount, String orderNo, String openId) {
        // 发起微信预支付
        WxPrePayForm form = new WxPrePayForm();
        form.setOutTradeNo(orderNo);
        form.setAmount(actualAmount);
        form.setOpenId(openId);
        form.setDescription("【牛大师团餐】- " + orderNo);
        log.debug("【调用微信预支付接口】- 开始: [{}]", form);
        PrepayWithRequestPaymentResponse prepayResponse = WxPayUtils.requestWxPrePay(form);
        log.debug("【调用微信预支付接口】- 结束: [{}]", prepayResponse);
        return prepayResponse;
    }

    private Refund wxApplyRefund(Order order) {
        CreateRequest createRequest = new CreateRequest();
        createRequest.setOutTradeNo(order.getOrderNo());
        createRequest.setOutRefundNo(OrderUtils.getInstance().generateOrder(CodeEnum.REFUND_ORDER, order.getUserId()));
        createRequest.setTransactionId(order.getTransactionId());
        AmountReq amountReq = new AmountReq();
        amountReq.setRefund(order.getActualAmount().longValue());
        amountReq.setTotal(order.getActualAmount().longValue());
        amountReq.setCurrency("CNY");
        createRequest.setAmount(amountReq);
        return WxPayUtils.applyRefund(createRequest);
    }

    /**
     * 保存订单
     *
     * @param createValidate 创建验证类
     * @param userId         用户ID
     * @param totalAmount    总金额
     * @param couponAmount   优惠券金额
     * @param actualAmount   实际金额
     */
    private void saveOrder(OrderCreateValidate createValidate,
                           List<MenuMeal> menuMeals,
                           Integer userId,
                           Integer totalAmount,
                           Integer couponAmount,
                           Integer actualAmount) {
        Order order = new Order();
        order.setOrderNo(createValidate.getOrderNo());
        order.setUserId(userId);
        order.setAddressId(createValidate.getAddressId());
        // 套餐数量
        List<OrderDetailValidate> orderMenuMealList =
                createValidate.getMenuMealList();
        int mealNumber = orderMenuMealList.stream().mapToInt(OrderDetailValidate::getQuantity).sum();
        order.setMealNumber(mealNumber);
        // 总金额
        order.setTotalAmount(totalAmount);
        order.setCouponId(createValidate.getCouponId());
        order.setCouponAmount(couponAmount);
        order.setActualAmount(actualAmount);
        order.setStatus(OrderStatusEnum.ORDERED.getCode());
        order.setIsDelete(0);
        order.setCreateTime(TimeUtils.timestamp());
        order.setVersion(0);
        orderMapper.insert(order);
        // 订单详情表
        List<Integer> mealId = menuMeals.stream().map(MenuMeal::getMealId).collect(Collectors.toList());
        List<OrderMeal> orderMeals = orderMealMapper.selectBatchIds(mealId);
        Map<Integer, List<MenuMeal>> menuMealMap = menuMeals.stream().collect(Collectors.groupingBy(MenuMeal::getId));
        Map<Integer, List<OrderMeal>> mealMap = orderMeals.stream().collect(Collectors.groupingBy(OrderMeal::getId));
        orderMenuMealList.forEach(orderMenuMeal -> {
            OrderDetail detail = new OrderDetail();
            detail.setOrderId(order.getId());
            MenuMeal menuMeal = menuMealMap.get(orderMenuMeal.getMenuMealId()).get(0);
            detail.setMenuId(menuMeal.getMenuId());
            detail.setMealId(menuMeal.getMealId());
            detail.setMenuMealId(menuMeal.getId());
            OrderMeal meal = mealMap.get(menuMeal.getMealId()).get(0);
            detail.setMealName(meal.getMealName());
            detail.setMealPic(meal.getMealPic());
            detail.setPrice(menuMeal.getPrice());
            detail.setPreferentialAmount(menuMeal.getPreferentialAmount());
            detail.setMealDescribe(meal.getMealDescribe());
            detail.setMealNumber(orderMenuMeal.getQuantity());
            detail.setUnitPrice(menuMeal.getPrice() - menuMeal.getPreferentialAmount());
            detail.setCreateTime(TimeUtils.timestamp());
            orderDetailMapper.insert(detail);
            // 减库存
            MenuMeal menuMealStock = menuMealMapper.selectById(menuMeal.getId());
            int num = menuMealStock.getStock() - orderMenuMeal.getQuantity();
            Assert.isTrue(num >= 0, "订单中【" + meal.getMealName() + "】库存不足, 请重新下单");
            menuMeal.setStock(num);
            // 去掉库存为0时，自动下架的状态
            /*if (num == 0) {
                menuMeal.setStatus(0);
            }*/
            menuMeal.setSalesNum(menuMeal.getSalesNum() + orderMenuMeal.getQuantity());
            menuMeal.setUpdateTime(TimeUtils.timestamp());
            menuMealMapper.updateById(menuMeal);
        });
        // 更新优惠券
        if (createValidate.getCouponId() != null) {
            Coupon coupon = couponMapper.selectById(createValidate.getCouponId());
            coupon.setStatus(1);
            coupon.setUseTime(TimeUtils.timestamp());
            couponMapper.updateById(coupon);
        }
    }
}
